package com.smsc.headend.connector.manager;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.lang.Tuple;
import com.smsc.headend.common.thread.UDISThreadFactory;
import com.smsc.headend.module.task.dto.ConnectorTask;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.*;

@Component
@Slf4j
public class TaskTicketManager {

    @Autowired
    @Qualifier("deviceTicketPool")
    LinkedBlockingQueue<Integer> deviceTicketPool;

    @Autowired
    @Qualifier("deviceTaskQueue")
    LinkedBlockingQueue<ConnectorTask> deviceTaskQueue;

    private ConcurrentHashMap<Integer, Tuple> ticketPublished = new ConcurrentHashMap<>();

    ScheduledExecutorService checkTaskService = Executors.newScheduledThreadPool(1, new UDISThreadFactory("register"));

    @PostConstruct
    public void initTask() {
        //检查channel中的任务时候正常返还到池中。 发行超过10分钟的ticketNumber，回收到池中
        checkTaskService.scheduleAtFixedRate(this::checkTaskTicket, 120, 30, TimeUnit.SECONDS);
    }

    private void checkTaskTicket() {
        if (CollectionUtils.isEmpty(ticketPublished)) {
            return;
        }
        log.info("checkExecutingTaskSize:{}", ticketPublished.size());
        ticketPublished.entrySet().stream().filter(ety -> SystemClock.now() - Convert.toLong(ety.getValue().get(1)) >= 10 * DateUtils.MILLIS_PER_MINUTE).forEach(this::releaseTicket);
    }

    private void releaseTicket(Map.Entry<Integer, Tuple> ticketTaskEntry) {
        Integer ticketNumber = ticketTaskEntry.getKey();
        ConnectorTask connectorTask = ticketTaskEntry.getValue().get(0);
        log.warn("ticketNumberReleaseRedundant:comId:{},taskNo:{}", connectorTask.getTerminalId(), connectorTask.getTaskNo());
        deviceTicketPool.add(ticketNumber);
    }

    public Integer getTicketOnce(ConnectorTask connectorTask) {
        Integer ticketNumber = deviceTicketPool.poll();
        if (ticketNumber == null) {
            return null;
        }
        Tuple taskAndTime = new Tuple(connectorTask, SystemClock.now());
        Tuple oldOne = ticketPublished.putIfAbsent(ticketNumber, taskAndTime);
        if (oldOne != null) {
            ConnectorTask connectorTask1 = oldOne.get(0);
            log.warn("GetTicketNumberWithTaskNotClearInGetOnce:ticket:{},oldTask:{},comId:{},getTicketTime:{}", ticketNumber, connectorTask1.getTaskNo(), connectorTask1.getTerminalId(), oldOne.get(1));
            return ticketNumber;
        }
        return ticketNumber;
    }

    public Integer tryGetTicket(ConnectorTask connectorTask) throws InterruptedException {
        Integer ticketNumber = null;
        Integer time = 0;
        while (ticketNumber == null && time < 10) {
            ticketNumber = deviceTicketPool.poll(15, TimeUnit.SECONDS);
            time++;
        }
        if (ticketNumber != null) {
            Tuple taskAndTime = new Tuple(connectorTask, SystemClock.now());
            Tuple oldOne = ticketPublished.putIfAbsent(ticketNumber, taskAndTime);
            if (oldOne != null) {
                ConnectorTask connectorTask1 = oldOne.get(0);
                log.warn("GetTicketNumberWithTaskNotClearInTryGetTicket:ticket:{},oldTask:{},comId:{},getTicketTime:{}", ticketNumber, connectorTask1.getTaskNo(), connectorTask1.getTerminalId(), oldOne.get(1));
                if (!deviceTicketPool.contains(ticketNumber)) {
                    deviceTicketPool.add(ticketNumber);
                }
                return null;
            }
        }
        return ticketNumber;
    }

    public void recycleTicket(Integer ticketNumber) {
        ticketPublished.remove(ticketNumber);
        if (!deviceTicketPool.contains(ticketNumber)) {
            deviceTicketPool.add(ticketNumber);
        }
    }

    public Integer getTicketPoolSize() {
        return deviceTicketPool.size();
    }


    public Boolean isTaskQueueEmpty() {
        return this.deviceTaskQueue.isEmpty();
    }

    public void addTaskToQueue(ConnectorTask connectorTask) {
        this.deviceTaskQueue.add(connectorTask);
    }

    public ConnectorTask pollTaskQueue() {
        return this.deviceTaskQueue.poll();
    }

    public Integer getTaskQueueSize() {
        return this.deviceTaskQueue.size();
    }
}
